////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
//  File name:   brush.cpp
//  Version:     v1.00
//  Created:     28/5/2001 by Vladimir Kajalin
//  Compilers:   Visual Studio.NET
//  Description: 
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include <ILMSerializationManager.h>
#include "StatObj.h"
#include "ObjMan.h"
#include "VisAreas.h"
#include "terrain_sector.h"
#include "CullBuffer.h"
#include "3dEngine.h"
#include "IndexedMesh.h"
#include "WaterVolumes.h"
#include "Brush.h"
#include "terrain.h"

const char * CBrush::GetEntityClassName() const
{
	return "Brush";
}

Vec3 CBrush::GetPos(bool bWorldOnly) const
{
	assert(bWorldOnly);
	return m_Matrix.GetTranslation();
}

const char *CBrush::GetName() const
{
	if (m_pStatObj)
		return m_pStatObj->GetFilePath();
	return "StatObjNotSet";
}

bool CBrush::HasChanged()
{
	return false;
}

#ifdef WIN64
#pragma warning( push )									//AMD Port
#pragma warning( disable : 4311 )
#endif

void CBrush::Render(const struct SRendParams & _EntDrawParams)
{
	FUNCTION_PROFILER_3DENGINE;

  if (!m_pStatObj || m_dwRndFlags&ERF_HIDDEN)
    return; //false;

	if (m_dwRndFlags & ERF_COLLISION_PROXY || m_dwRndFlags & ERF_RAYCAST_PROXY)
	{
		// Collision proxy is visible in Editor while in editing mode.
		if (!gEnv->IsEditor() || !gEnv->IsEditing())
		{
			if (GetCVars()->e_DebugDraw == 0)
				return; //true;
		}
	}

	// some parameters will be modified
	SRendParams rParms = _EntDrawParams;

#ifdef SUPPORT_LM
	// enable lightmaps if allowed
	if(m_dwRndFlags&ERF_USERAMMAPS && HasLightmap(0) && GetCVars()->e_ram_maps)
		rParms.m_pLMData	=	GetLightmapData(0);
  else
    rParms.m_pLMData	=	0;
#endif // SUPPORT_LM

  if(m_nMaterialLayers)
    rParms.nMaterialLayers = m_nMaterialLayers;

	rParms.pMatrix = &m_Matrix;
	rParms.nVisAreaStencilRef = 0;
	rParms.pMaterial = m_pMaterial;
  rParms.ppRNTmpData = &m_pRNTmpData;
/*
  float fRadius = GetBBox().GetRadius();

  if(m_dwRndFlags&ERF_MERGE_RESULT)
  { // merged vegetations
    rParms.dwFObjFlags |= FOB_VEGETATION;
    rParms.dwFObjFlags &= ~FOB_TRANS_MASK;   
    if(m_dwRndFlags&ERF_USE_TERRAIN_COLOR && rParms.pTerrainTexInfo)
    {
      rParms.dwFObjFlags |= FOB_BLEND_WITH_TERRAIN_COLOR;
      Vec3 vTerrainNormal = GetTerrain()->GetTerrainSurfaceNormal(GetBBox().GetCenter(), fRadius);
      uint8 ucSunDotTerrain	 = (uint8)(CLAMP((vTerrainNormal.Dot(Get3DEngine()->GetSunDirNormalized()))*255.f, 0, 255));
      rParms.AmbientColor.a *= (1.0f / 255.f * ucSunDotTerrain);
    }
  }
	*/
  if( !m_Matrix.m01 && !m_Matrix.m02 && !m_Matrix.m10 && !m_Matrix.m12 && !m_Matrix.m20 && !m_Matrix.m21 )
    rParms.dwFObjFlags &= ~FOB_TRANS_ROTATE;
  else
    rParms.dwFObjFlags |= FOB_TRANS_ROTATE;

  // get statobj for rendering
  IStatObj * pStatObj = m_pStatObj;

  // cull lights per triangle for big objects
//  if(GetCVars()->e_brushes_cull_lights_per_triangle_min_obj_radius && pStatObj->GetRenderMesh() &&
  //  fRadius > GetCVars()->e_brushes_cull_lights_per_triangle_min_obj_radius)
//    CObjManager::CullLightsPerTriangle(pStatObj->GetRenderMesh(), m_Matrix, rParms.nDLightMask, m_lightsCache);

	// render
	if(pStatObj)
	{
#ifdef SUPPORT_LM
		if (pStatObj->m_nFlags&STATIC_OBJECT_COMPOUND)
			rParms.m_pLMData	=	(m_SubObjectLightmapData.size()==pStatObj->SubObjectCount())?&m_SubObjectLightmapData[0]:0;
#endif // SUPPORT_LM

		//if(GetCVars()->e_ram_maps && rParms.m_pLMData && rParms.m_pLMData->m_pLMData && rParms.m_pLMData->m_pLMData->GetRAETex())
		//	rParms.dwFObjFlags |= FOB_RAE_GEOMTERM;
		//else
		//	rParms.dwFObjFlags &= ~FOB_RAE_GEOMTERM;

		pStatObj->Render(rParms);
	}
}

void CBrush::Render(SRenderObjectModifier * pROM, int nThreadId) 
{
  FUNCTION_PROFILER_3DENGINE;

  // Collision proxy is visible in Editor while in editing mode.
  if(m_dwRndFlags & ERF_COLLISION_PROXY)
    if (!gEnv->IsEditor() || !gEnv->IsEditing())
      if (GetCVars()->e_DebugDraw == 0)
        return;

  if(m_dwRndFlags & ERF_HIDDEN)
    return;

	if(m_dwRndFlags & ERF_SELECTED)
		m_fLastSelectedTime = Get3DEngine()->GetCurTimeSec();

  if(GetIntegrationType() && (!(m_fLastSelectedTime > Get3DEngine()->GetCurTimeSec()-2.f) || !GetCVars()->e_VoxTerOnTheFlyIntegration))
  {
    if( GetCVars()->e_Voxel && GetCVars()->e_VoxTerHideIntegrated==2 )
      return; // hide always
    if( GetCVars()->e_Voxel && GetCVars()->e_VoxTerHideIntegrated && Get3DEngine()->GetIVoxTerrain() )
      return; // hide only if voxel terrain used    
  }

  CRenderObject * & pObj = m_pRNTmpData->userData.pRenderObject;

	//////////////////////////////////////////////////////////////////////////
	// temp fix to update ambient color (Vlad please review!)

	if(!Get3DEngine()->IsRenderingIntoShadowMap() && !m_nRenderStackLevel)
	{
		pObj->m_nVisAreaStencilRef = 0;
		if(m_pOcNode && m_pOcNode->m_pVisArea)
		{
			pObj->m_II.m_AmbColor = m_pOcNode->m_pVisArea->GetFinalAmbientColor();
			pObj->m_nVisAreaStencilRef = gEnv->pRenderer->EF_GetVisAreaStencilRef( m_pOcNode->m_pVisArea );
		}
			else 
				pObj->m_II.m_AmbColor = Get3DEngine()->GetSkyColor();
	}
	//////////////////////////////////////////////////////////////////////////
	
	if (m_dwRndFlags & ERF_RECVWIND) 
	{ 
		if(CStatObj * pStatObj = (CStatObj *)GetEntityStatObj())
		{
			m_pRNTmpData->userData.m_fMainBendingScale = m_fBending;
			pStatObj->SetupBending(pObj, this, true);
		}
	}

	//////////////////////////////////////////////////////////////////////////

	const Vec3 vCamPos = GetCamera().GetPosition();
	const Vec3 vObjCenter = GetBBox().GetCenter();
	const Vec3 vObjPos = GetPos();

	// check the object against the water level
	if(CObjManager::IsAfterWater(vObjCenter, vCamPos, Get3DEngine()->GetWaterLevel()))
		pObj->m_ObjFlags |= FOB_AFTER_WATER;
	else
		pObj->m_ObjFlags &= ~FOB_AFTER_WATER;

  if(CStatObj * pStatObj = (CStatObj *)GetEntityStatObj())
  {
		uint8 nDissolveRefA;
		int nLodA, nLodB;

		if(GetCVars()->e_Dissolve && !m_nRenderStackLevel && !Get3DEngine()->IsRenderingIntoShadowMap() && !(pStatObj->m_nFlags & STATIC_OBJECT_COMPOUND))
		{
			int nLod;

			{
				nLod = CLAMP(m_pRNTmpData->userData.nLod, pStatObj->GetMinUsableLod(), (int)pStatObj->m_nMaxUsableLod);
				nLod = pStatObj->FindNearesLoadedLOD(nLod, true);
			}

			SLodDistDissolveTransitionState *pState = &m_pRNTmpData->userData.lodDistDissolveTransitionState;

			// if we're more than one LOD away we've either zoomed in quickly
			// or we streamed in some LODs really late. In either case we want
			// to pop rather than get stuck in a dissolve that started way too late.
			if(pState->nOldLod >= 0 && nLod >= 0 && 
							(pState->nOldLod > nLod+1 || pState->nOldLod < nLod-1)
				)
				pState->nOldLod = nLod;

			// when we first load before streaming we get a lod of -1. When a lod streams in
			// we kick off a transition to N, but without moving there's nothing to continue the transition.
			// Catch this case when we claim to be in lod -1 but are too close, and snap.
			if(pState->nOldLod == -1 && pObj->m_fDistance < 0.5f*m_fWSMaxViewDist)
				pState->nOldLod = nLod;

			uint32 prevState = (((uint32)pState->nOldLod) << 8) | pState->nNewLod;

			float fDissolve = GetObjManager()->GetLodDistDissolveRef(pState, pObj->m_fDistance, nLod);

			uint32 newState = (((uint32)pState->nOldLod) << 8) | pState->nNewLod;

			// ensure old lod is still around. If not find closest lod
			if(pState->nOldLod != pState->nNewLod && pState->nOldLod >= 0)
			{
				pState->nOldLod = pStatObj->FindNearesLoadedLOD(pState->nOldLod, true);
			}
			else if(pState->nOldLod >= 0)
			{
				// we can actually fall back into this case (even though we know nLod is valid).
				pState->nOldLod = pState->nNewLod = pStatObj->FindNearesLoadedLOD(pState->nOldLod, true);
			}

			// only bother to check if we are dissolving and we've just kicked off a new dissolve transition
			if(pState->nOldLod != pState->nNewLod && prevState != newState)
			{
				// LOD cutoff point, this is about where the transition should be triggered.
				float dist = CObjManager::GetObjectLODDist(max(pState->nOldLod, pState->nNewLod), GetLodRatioNormalized(), GetBBox().GetRadius());

				// we started way too late, most likely object LOD streamed in very late, just snap.
				if(fabsf(pState->fStartDist - dist) > GetFloatCVar(e_DissolveDistband))
				{
					pState->nOldLod = pState->nNewLod;
				}
			}

			nDissolveRefA = (uint8)(255.f * SATURATE(fDissolve));
			nLodA = pState->nOldLod;
			nLodB = pState->nNewLod;
		}
		else
		{
			nDissolveRefA = 0;

			nLodA = CLAMP(m_pRNTmpData->userData.nLod, pStatObj->GetMinUsableLod(), (int)pStatObj->m_nMaxUsableLod);
			nLodA = pStatObj->FindNearesLoadedLOD(nLodA);
			nLodB = -1;
		}

		// update LOD faster in zoom mode
		if(!m_nRenderStackLevel && m_nZoomMode)
		{
			float fEntDistance = cry_sqrtf(Distance::Point_AABBSq(vCamPos,GetBBox()))*m_fZoomFactor;
			m_pRNTmpData->userData.nLod = CObjManager::GetObjectLOD(fEntDistance, GetLodRatioNormalized(), GetBBox().GetRadius());
		}

#if !defined (XENON) && !defined(PS3)
		// temporary fix for autoreload from max export, Vladimir needs to properly fix it!
		if (pObj->m_pCurrMaterial != GetMaterial())
			pObj->m_pCurrMaterial = GetMaterial();
#endif

		if(GetCVars()->e_Dissolve)
		{
			float fDissolveDist = CLAMP(0.1f*m_fWSMaxViewDist, GetFloatCVar(e_DissolveDistMin), GetFloatCVar(e_DissolveDistMax));
    
			const float fDissolveStartDist = m_fWSMaxViewDist-fDissolveDist;

			if(pObj->m_fDistance > fDissolveStartDist)
			{
				float fDissolve = (pObj->m_fDistance - fDissolveStartDist)
					/ fDissolveDist;
				nDissolveRefA = (uint8)(255.f * SATURATE(fDissolve));
				nLodB = -1;
			}
		}

		if (Get3DEngine()->IsTessellationAllowed(pObj))
		{
			// Allow this RO to be tessellated, however actual tessellation will be applied if enabled in material
			pObj->m_ObjFlags |= FOB_ALLOW_TESSELLATION;
		}
		else pObj->m_ObjFlags &= ~FOB_ALLOW_TESSELLATION;

		pStatObj->RenderInternal(pObj, pROM, 0, nThreadId, nLodA, nDissolveRefA, nLodB, 255-nDissolveRefA);

		if (GetCVars()->e_BBoxes && !Get3DEngine()->IsRenderingIntoShadowMap())
      GetObjManager()->RenderObjectDebugInfo((IRenderNode*)this, pObj);
  }
}

#ifdef WIN64
#pragma warning( pop )									//AMD Port
#endif

struct IStatObj * CBrush::GetEntityStatObj( unsigned int nPartId, unsigned int nSubPartId, Matrix34A* pMatrix, bool bReturnOnlyVisible )
{
	if(nPartId != 0)
		return 0;

	if(pMatrix)
		*pMatrix = m_Matrix;

	return m_pStatObj;
}

void CBrush::SetMatrix( const Matrix34& mat )
{
	Get3DEngine()->UnRegisterEntity(this);

	bool replacePhys = false; 

	if(!IsMatrixValid(mat))
	{
		Warning( "Error: IRenderNode::SetMatrix: Invalid matrix passed from the editor - ignored, reset to identity: %s", GetName());
    replacePhys = true; 
		m_Matrix.SetIdentity();
	}
	else
  {
    replacePhys = fabs(mat.GetColumn(0).len()-m_Matrix.GetColumn(0).len())
			          + fabs(mat.GetColumn(1).len()-m_Matrix.GetColumn(1).len())
			          + fabs(mat.GetColumn(2).len()-m_Matrix.GetColumn(2).len()) > FLT_EPSILON; 
		m_Matrix = mat;
  }

	CalcBBox();

	Get3DEngine()->RegisterEntity(this);
	if (replacePhys) 
		Dephysicalize();
	if (!m_pPhysEnt)
		Physicalize();
	else
	{
		// Just move physics.
		pe_status_placeholder spc;
		if (m_pPhysEnt->GetStatus(&spc) && !spc.pFullEntity)
		{
			pe_params_bbox pbb;
			pbb.BBox[0]=m_WSBBox.min; pbb.BBox[1]=m_WSBBox.max;
			m_pPhysEnt->SetParams(&pbb);
		} else
		{
			pe_params_pos par_pos;
			par_pos.pMtx3x4 = &m_Matrix;
			m_pPhysEnt->SetParams(&par_pos);
		}

		//////////////////////////////////////////////////////////////////////////
		// Update physical flags.
		//////////////////////////////////////////////////////////////////////////
		pe_params_foreign_data  foreignData;
		m_pPhysEnt->GetParams(&foreignData);
		if (m_dwRndFlags & ERF_HIDABLE)
			foreignData.iForeignFlags |= PFF_HIDABLE;
		else
			foreignData.iForeignFlags &= ~PFF_HIDABLE;
		if (m_dwRndFlags & ERF_HIDABLE_SECONDARY)
			foreignData.iForeignFlags |= PFF_HIDABLE_SECONDARY;
		else
			foreignData.iForeignFlags &= ~PFF_HIDABLE_SECONDARY;
		// flag to exclude from AI triangulation
		if (m_dwRndFlags & ERF_EXCLUDE_FROM_TRIANGULATION)
			foreignData.iForeignFlags |= PFF_EXCLUDE_FROM_STATIC;
		else
			foreignData.iForeignFlags &= ~PFF_EXCLUDE_FROM_STATIC;
		m_pPhysEnt->SetParams(&foreignData);
	}

	if(GetCVars()->e_VoxTer && GetIntegrationType() && m_bEditor)
		PreCopyResources();
}

void CBrush::CalcBBox()
{
	m_WSBBox.min = SetMaxBB();
	m_WSBBox.max = SetMinBB();

	if (!m_pStatObj)
		return;

	m_WSBBox.min = m_pStatObj->GetBoxMin();
	m_WSBBox.max = m_pStatObj->GetBoxMax();
	m_WSBBox.SetTransformedAABB(m_Matrix,m_WSBBox);
  m_fMatrixScale = m_Matrix.GetColumn0().GetLength();
}

CBrush::CBrush()
{
	m_WSBBox.min=m_WSBBox.max=Vec3(ZERO);
	m_dwRndFlags=0;
	m_Matrix.SetIdentity();
	m_pPhysEnt=0;
	m_Matrix.SetIdentity();
	m_pMaterial = 0;
	m_nMergeGroupId = 0;
  m_nLayerId = 0;
	m_pStatObj = NULL;
  m_pMaterial = NULL;
	m_bVehicleOnlyPhysics = false;
  m_bMerged = 0;
  m_fMatrixScale = 1.f;
	m_fLastSelectedTime = 0;

	GetInstCount(GetRenderNodeType())++;
}

void CBrush::DeleteLMTC()
{
#ifdef SUPPORT_LM
	for(int nLod=0;nLod<MAX_BRUSH_LODS_NUM;nLod++)
		if(m_arrLMData[nLod].m_pLMTCBuffer)
		{
			gEnv->pRenderer->DeleteRenderMesh(m_arrLMData[nLod].m_pLMTCBuffer);
			m_arrLMData[nLod].m_pLMTCBuffer = NULL;
		}
		for(int a=0;a<m_SubObjectLightmapData.size();a++)
			if(m_SubObjectLightmapData[a].m_pLMTCBuffer)
			{
				gEnv->pRenderer->DeleteRenderMesh(m_SubObjectLightmapData[a].m_pLMTCBuffer);
				m_SubObjectLightmapData[a].m_pLMTCBuffer = NULL;
			}
#endif // SUPPORT_LM
}

CBrush::~CBrush()
{  
	Dephysicalize( );
//	Get3DEngine()->UnRegisterEntity(this);
  Get3DEngine()->FreeRenderNodeState(this);
  
	//m_pLMData = NULL;
/*
	if(GetRndFlags()&ERF_MERGED_NEW && m_nObjectTypeID>=0)
	{
		CStatObj * pBody = (CStatObj*)GetObjManager()->m_lstBrushTypes[m_nObjectTypeID];
		assert(pBody->m_nUsers==1);
		delete pBody;
		GetObjManager()->m_lstBrushTypes[m_nObjectTypeID] = NULL;
		m_nObjectTypeID=-1;
	}
*/
  DeleteLMTC();
	
  m_pStatObj = NULL;

	if(m_pRNTmpData)
		Get3DEngine()->FreeRNTmpData(&m_pRNTmpData);
	assert(!m_pRNTmpData);

  GetInstCount(GetRenderNodeType())--;
}

void CBrush::Physicalize(bool bInstant)
{
	float fScaleX = m_Matrix.GetColumn(0).len();
	float fScaleY = m_Matrix.GetColumn(1).len();
	float fScaleZ = m_Matrix.GetColumn(2).len();

	if( fabs(fScaleX - fScaleY)>0.01f || fabs(fScaleX - fScaleZ)>0.01f || !m_pStatObj || !m_pStatObj->IsPhysicsExist() )
	{ // scip non uniform scaled object and object without physics
		
		// Check if we are compound object.
		if (m_pStatObj && !m_pStatObj->IsPhysicsExist() && (m_pStatObj->GetFlags() & STATIC_OBJECT_COMPOUND))
		{
			// Try to physicalize compound object.
		}
		else
		{
			Dephysicalize();
			return;
		}
	}

	/*if (!GetCVars()->e_OnDemandPhysics || pBody->GetFlags() & STATIC_OBJECT_COMPOUND)
		bInstant = true;

	if (!bInstant)
	{
		pe_status_placeholder spc;
		if (m_pPhysEnt && m_pPhysEnt->GetStatus(&spc) && spc.pFullEntity)
			GetSystem()->GetIPhysicalWorld()->DestroyPhysicalEntity(spc.pFullEntity);

		pe_params_bbox pbb;
		pbb.BBox[0] = m_WSBBox.min;
		pbb.BBox[1] = m_WSBBox.max;
		pe_params_foreign_data pfd;
		pfd.pForeignData = (IRenderNode*)this;
		pfd.iForeignData = PHYS_FOREIGN_ID_STATIC;
		pfd.iForeignFlags = PFF_BRUSH;

		if (!m_pPhysEnt)
			m_pPhysEnt = GetSystem()->GetIPhysicalWorld()->CreatePhysicalPlaceholder(PE_STATIC,&pbb);
		else
			m_pPhysEnt->SetParams(&pbb);
		m_pPhysEnt->SetParams(&pfd);
		return;
	}*/

//  CStatObj * pBody = (CStatObj*)GetObjManager()->m_lstBrushTypes[m_nObjectTypeID];
//  m_pStatObj->CheckLoaded();
//  if(!(pBody && (pBody->m_arrPhysGeomInfo[0] || pBody->m_arrPhysGeomInfo[1])))
  //  return;

	//Dephysicalize( );
	// create new
//	pe_params_pos par_pos;
	//pe_status_placeholder spc;
	//int bOnDemandCallback = 0;
	if (!m_pPhysEnt)
	{
		m_pPhysEnt = GetSystem()->GetIPhysicalWorld()->CreatePhysicalEntity(PE_STATIC,NULL,(IRenderNode*)this,PHYS_FOREIGN_ID_STATIC);
		if(!m_pPhysEnt)
			return;
	}
	/*else if (bOnDemandCallback = m_pPhysEnt->GetStatus(&spc) && spc.pFullEntity==0) 
		GetSystem()->GetIPhysicalWorld()->CreatePhysicalEntity(PE_STATIC,5.0f,0,0,0,-1,m_pPhysEnt);*/

	pe_action_remove_all_parts remove_all;
	m_pPhysEnt->Action(&remove_all);//,bOnDemandCallback);

  pe_geomparams params;	  
	if(m_pStatObj->GetPhysGeom(PHYS_GEOM_TYPE_DEFAULT))
  {
		if (GetRndFlags() & ERF_COLLISION_PROXY)
		{
			// Collision proxy only collides with players and vehicles.
			params.flags = geom_colltype_player | geom_colltype_vehicle;
		}
		if (GetRndFlags() & ERF_RAYCAST_PROXY)
		{
			// Collision proxy only collides with players and vehicles.
			params.flags = geom_colltype_ray;
		}
		/*if (m_pStatObj->m_arrPhysGeomInfo[PHYS_GEOM_TYPE_NO_COLLIDE])
			params.flags &= ~geom_colltype_ray;*/
		if (m_bVehicleOnlyPhysics || (m_pStatObj->GetVehicleOnlyPhysics()!=0))
				params.flags = geom_colltype_vehicle;
		if (GetCVars()->e_ObjQuality != CONFIG_LOW_SPEC) 
		{
			params.idmatBreakable = m_pStatObj->GetIDMatBreakable();
			if (m_pStatObj->GetBreakableByGame())
				params.flags |= geom_manually_breakable;
		}	else
			params.idmatBreakable = -1;
  }

  Matrix34 mtxScale; mtxScale.SetScale(Vec3(fScaleX, fScaleY, fScaleZ));
  params.pMtx3x4 = &mtxScale; 
	m_pStatObj->Physicalize(m_pPhysEnt, &params);

  if(m_dwRndFlags & (ERF_HIDABLE|ERF_HIDABLE_SECONDARY|ERF_EXCLUDE_FROM_TRIANGULATION))
  {
    pe_params_foreign_data  foreignData;
    m_pPhysEnt->GetParams(&foreignData);
		if (m_dwRndFlags & ERF_HIDABLE)
			foreignData.iForeignFlags |= PFF_HIDABLE;
		if (m_dwRndFlags & ERF_HIDABLE_SECONDARY)
			foreignData.iForeignFlags |= PFF_HIDABLE_SECONDARY;
		//[PETAR] new flag to exclude from triangulation
		if (m_dwRndFlags & ERF_EXCLUDE_FROM_TRIANGULATION)
			foreignData.iForeignFlags |= PFF_EXCLUDE_FROM_STATIC;
    m_pPhysEnt->SetParams(&foreignData);//,bOnDemandCallback);
  }

	pe_params_flags par_flags;
	par_flags.flagsOR = pef_never_affect_triggers | pef_log_state_changes;
	m_pPhysEnt->SetParams(&par_flags);//,bOnDemandCallback);

  pe_params_pos par_pos;
	par_pos.pMtx3x4 = &m_Matrix;
	par_pos.bEntGridUseOBB = 1;
	m_pPhysEnt->SetParams(&par_pos);//,bOnDemandCallback);

	if (m_pMaterial)
		UpdatePhysicalMaterials( );//bOnDemandCallback );
}

bool CBrush::PhysicalizeFoliage(bool bPhysicalize, int iSource, int nSlot)
{
	if (nSlot<0)
	{
		bool res=false;
		for(int i=0; i<m_pStatObj->GetSubObjectCount(); i++)
			res = res || PhysicalizeFoliage(bPhysicalize,iSource,i);
		return res;
	}

	if (IStatObj::SSubObject *pSubObj = m_pStatObj->GetSubObject(nSlot))
		if (bPhysicalize)
		{
			if (!pSubObj->pStatObj || !((CStatObj*)pSubObj->pStatObj)->m_nSpines)
				return false;
			if (!(m_pStatObj->GetFlags() & STATIC_OBJECT_CLONE))
			{
				m_pStatObj = m_pStatObj->Clone(false,false,false);
				pSubObj = m_pStatObj->GetSubObject(nSlot);
			}
			Matrix34 mtx = m_Matrix*pSubObj->localTM;
			pSubObj->pStatObj->PhysicalizeFoliage(m_pPhysEnt, mtx, pSubObj->pFoliage, GetCVars()->e_FoliageBranchesTimeout, iSource);
			return pSubObj->pFoliage!=0;
		}	
		else if (pSubObj->pFoliage)
		{
			pSubObj->pFoliage->Release();
			return true;
		}
	return false;
}

IFoliage *CBrush::GetFoliage(int nSlot)
{
	if (IStatObj::SSubObject *pSubObj = m_pStatObj->GetSubObject(nSlot))
		return pSubObj->pFoliage;
	return 0;
}

//////////////////////////////////////////////////////////////////////////
void CBrush::UpdatePhysicalMaterials(int bThreadSafe)
{
	if (!m_pPhysEnt)
		return;

	if ((GetRndFlags() & ERF_COLLISION_PROXY) && m_pPhysEnt)
	{
		pe_params_part ppart;
		ppart.flagsAND = 0;
		ppart.flagsOR = geom_colltype_player | geom_colltype_vehicle;
		m_pPhysEnt->SetParams( &ppart );
	}

	if ((GetRndFlags() & ERF_RAYCAST_PROXY) && m_pPhysEnt)
	{
		pe_params_part ppart;
		ppart.flagsAND = 0;
		ppart.flagsOR = geom_colltype_ray;
		m_pPhysEnt->SetParams( &ppart );
	}
	
	if (m_pMaterial)
	{
		// Assign custom material to physics.
		int surfaceTypesId[MAX_SUB_MATERIALS];
		memset( surfaceTypesId,0,sizeof(surfaceTypesId) );
		int i,numIds = m_pMaterial->FillSurfaceTypeIds(surfaceTypesId);
		ISurfaceTypeManager *pSurfaceTypeManager = Get3DEngine()->GetMaterialManager()->GetSurfaceTypeManager();
		ISurfaceType *pMat;
		bool bBreakable = false;

		for(i=0,m_bVehicleOnlyPhysics=false; i<numIds; i++)
			if (pMat=pSurfaceTypeManager->GetSurfaceType(surfaceTypesId[i]))
			{
				if (pMat->GetFlags() & SURFACE_TYPE_VEHICLE_ONLY_COLLISION)
					m_bVehicleOnlyPhysics = true;
				if (pMat->GetBreakability())
					bBreakable = true;
			}
	
		if (bBreakable && m_pStatObj)
		{
			// mark the rendermesh as KepSysMesh so that it is kept in system memory
			if( m_pStatObj->GetRenderMesh() )
				m_pStatObj->GetRenderMesh()->KeepSysMesh(true);
			
			m_pStatObj->SetFlags( m_pStatObj->GetFlags() | STATIC_OBJECT_DYNAMIC);
		}
		pe_params_part ppart;
		ppart.nMats = numIds;
		ppart.pMatMapping = surfaceTypesId;
		if (m_bVehicleOnlyPhysics)
			ppart.flagsAND = geom_colltype_vehicle;
		m_pPhysEnt->SetParams( &ppart, bThreadSafe );
	}	else if (m_bVehicleOnlyPhysics)
	{
		m_bVehicleOnlyPhysics = false;
		if (!m_pStatObj->GetVehicleOnlyPhysics())
		{
			pe_params_part ppart;
			ppart.flagsOR = geom_colltype_solid|geom_colltype_ray|geom_floats|geom_colltype_explosion;
			m_pPhysEnt->SetParams( &ppart, bThreadSafe );
		}
	}
}

void CBrush::Dephysicalize( bool bKeepIfReferenced )
{
	// delete old physics
	if(m_pPhysEnt && 0 != GetSystem()->GetIPhysicalWorld()->DestroyPhysicalEntity(m_pPhysEnt, ((int)bKeepIfReferenced)*4))
		m_pPhysEnt=0;
}

void CBrush::Dematerialize( )
{
  if(m_pMaterial)
    m_pMaterial = 0;
}

IPhysicalEntity* CBrush::GetPhysics() const
{
	return m_pPhysEnt;
}

//////////////////////////////////////////////////////////////////////////
void CBrush::SetPhysics( IPhysicalEntity* pPhys )
{
	m_pPhysEnt = pPhys;
}

//////////////////////////////////////////////////////////////////////////
bool CBrush::IsMatrixValid(const Matrix34 & mat)
{
	Vec3 vScaleTest = mat.TransformVector(Vec3(0,0,1));
  float fDist = mat.GetTranslation().GetDistance(Vec3(0,0,0));

	if( vScaleTest.GetLength()>1000.f || vScaleTest.GetLength()<0.01f || fDist > 256000 ||
		!_finite(vScaleTest.x) || !_finite(vScaleTest.y) || !_finite(vScaleTest.z) )
		return false;

	return true;
}

//////////////////////////////////////////////////////////////////////////
void CBrush::SetMaterial( IMaterial *pMat )
{
  m_pMaterial = pMat;
	
	bool bCollisionProxy = false;
	bool bRaycastProxy = false;

	if (pMat)
	{
		if (pMat->GetFlags() & MTL_FLAG_COLLISION_PROXY)
			bCollisionProxy = true;

		if (pMat->GetFlags() & MTL_FLAG_RAYCAST_PROXY)
		{
			bRaycastProxy = true;
		}
	}
	SetRndFlags( ERF_COLLISION_PROXY,bCollisionProxy );
	SetRndFlags( ERF_RAYCAST_PROXY, bRaycastProxy);

	UpdatePhysicalMaterials();

  if (pMat != NULL &&  pMat->IsSubSurfScatterCaster())
  {
    m_dwRndFlags |= ERF_SUBSURFSCATTER;
    //force creating depth maps
    m_dwRndFlags |= ERF_CASTSHADOWMAPS;

		if(m_pOcNode)
			((COctreeNode*)m_pOcNode)->MarkAsUncompiled();
  }

	// register and get brush material id
	m_pMaterial = pMat;

	if(GetCVars()->e_VoxTer && GetIntegrationType() && m_bEditor)
		PreCopyResources();
}

//////////////////////////////////////////////////////////////////////////
IMaterial* CBrush::GetMaterial(Vec3 * pHitPos)
{
	if (m_pMaterial)
		return m_pMaterial;

	if(m_pStatObj)
		return m_pStatObj->GetMaterial();

	return NULL;
}

void CBrush::CheckPhysicalized()
{
//  if(GetEntityStatObj())
  //  ((CStatObj*)GetEntityStatObj())->CheckLoaded(true);

  if(!m_pPhysEnt)
    Physicalize();
}

float CBrush::GetMaxViewDist()
{
	if (GetMinSpecFromRenderNodeFlags(m_dwRndFlags) == CONFIG_DETAIL_SPEC)
		return max(GetCVars()->e_ViewDistMin,min(GetFloatCVar(e_ViewDistCompMaxSize), GetBBox().GetRadius())*GetCVars()->e_ViewDistRatioDetail*GetViewDistRatioNormilized());

  return max(GetCVars()->e_ViewDistMin,min(GetFloatCVar(e_ViewDistCompMaxSize), GetBBox().GetRadius())*GetCVars()->e_ViewDistRatio*GetViewDistRatioNormilized());
}
/*
void CBrush::Serialize(bool bSave, ICryPak * pPak, FILE * f)
{
#if 0

  if(bSave)
  {
    pPak->FWrite(this,sizeof(*this),1,f);

    if(0 && m_pLMTCBuffer)
    {
		*/
  /*    int nPos=0;
      m_pLMTCBuffer->Serialize(nPos,0,true,0,0);
      uint8 * pLMLBuffer = new uint8[nPos];
      pPak->FWrite(&nPos,sizeof(nPos),1,f);
      nPos=0;
      m_pLMTCBuffer->Serialize(nPos,pLMLBuffer,true,0,0);*/
/*
      // Make RenderMesh and fill it with texture coordinates
//      m_pLMTCBuffer = GetRenderer()->CreateRenderMeshInitialized(
  //      &vTexCoord3[0], pRenderMesh->GetSysVertCount(), VERTEX_FORMAT_P3F, 
    //    pRenderMesh->GetIndices(0), pRenderMesh->GetIndicesCount(), R_PRIMV_TRIANGLES, "LMapTexCoords", eRMT_Static);

      pPak->FWrite(&m_pLMTCBuffer->GetSysVertCount(),sizeof(m_pLMTCBuffer->GetSysVertCount()),1,f);
      byte * pData = (byte *)m_pLMTCBuffer->GetSysVertBuffer()->m_VS[VSF_GENERAL].m_VData;
      assert(m_VertexSize[m_pLMTCBuffer->GetSysVertBuffer()->m_vertexformat] == 12);
      int nSize = m_VertexSize[m_pLMTCBuffer->GetSysVertBuffer()->m_vertexformat]*m_pLMTCBuffer->GetSysVertCount();
      pPak->FWrite(&m_pLMTCBuffer->GetSysVertCount(),sizeof(m_pLMTCBuffer->GetSysVertCount()),1,f);
    }
    else
    {
      int nPos=0;
      pPak->FWrite(&nPos,sizeof(nPos),1,f);
    }
  }
  else
  {
    pPak->FRead(this,sizeof(*this),1,f);

    if(m_nMaterialId>=0)
    { // restore material
      const char * sMtlName = CBrush::m_lstSExportedBrushMaterials[m_nMaterialId].material;
			m_pMaterial = Get3DEngine()->LoadMaterial( sMtlName );
    }
    else
      m_pMaterial = 0;

    int nSize=0;
    pPak->FRead(&nSize,sizeof(nSize),1,f);

    if(nSize)
    {
      assert(0);
      int nVertCount = 0;
      pPak->FRead(&nVertCount,sizeof(nVertCount),1,f);
      byte * pData = new byte[nVertCount*12];
      pPak->FRead(pData,nVertCount*12,1,f);

      // Make RenderMesh and fill it with texture coordinates
      m_pLMTCBuffer = GetRenderer()->CreateRenderMeshInitialized(
        pData, nVertCount, VERTEX_FORMAT_P3F, 
        0, 0, R_PRIMV_TRIANGLES, "LMapTexCoordsStreamed", eRMT_Static);

      assert(m_VertexSize[m_pLMTCBuffer->GetSysVertBuffer()->m_vertexformat] == 12);
    }
    else
    {
      m_pLMTCBuffer = 0;
      m_pLMData = NULL;
    }
  }

#endif
}*/

void CBrush::GetMemoryUsage(ICrySizer * pSizer) const
{
	SIZER_COMPONENT_NAME(pSizer, "Brush");
	pSizer->AddObject(this, sizeof(*this));
}

void CBrush::SetEntityStatObj( unsigned int nSlot, IStatObj * pStatObj, const Matrix34A * pMatrix )
{
  //assert(pStatObj);

	IStatObj *pPrevStatObj = m_pStatObj;

  m_pStatObj = (CStatObj*)pStatObj;

  // If object differ we must re-physicalize.
	if (pStatObj != pPrevStatObj)
		if (!pPrevStatObj || !pStatObj || (pStatObj->GetCloneSourceObject()!=pPrevStatObj))// ||
				//(pStatObj->GetFlags() & (STATIC_OBJECT_GENERATED|STATIC_OBJECT_CLONE))==STATIC_OBJECT_CLONE)
			Physicalize();

  if(pMatrix)
    SetMatrix(*pMatrix);

	if(m_pRNTmpData)
		Get3DEngine()->FreeRNTmpData(&m_pRNTmpData);
	assert(!m_pRNTmpData);

	m_nInternalFlags |= UPDATE_DECALS;
}

IRenderNode * CBrush::Clone() const
{
	CBrush * pDestBrush		= new CBrush();

	//CBrush member vars
	//	potential issues with Smart Pointers
	pDestBrush->m_Matrix				= m_Matrix;
	pDestBrush->m_fMatrixScale	= m_fMatrixScale;
	//pDestBrush->m_pPhysEnt		//Don't want to copy the phys ent pointer
	pDestBrush->m_pMaterial			= m_pMaterial;
	pDestBrush->m_nMergeGroupId	= m_nMergeGroupId;
	pDestBrush->m_pStatObj			= m_pStatObj;

	pDestBrush->m_bVehicleOnlyPhysics	= m_bVehicleOnlyPhysics;
	pDestBrush->m_bMerged							= m_bMerged;
	pDestBrush->m_WSBBox							= m_WSBBox;
	pDestBrush->m_fLastSelectedTime		= m_fLastSelectedTime;

	//IRenderNode member vars
	//	We cannot just copy over due to issues with the linked list of IRenderNode objects
	CopyIRenderNodeData(pDestBrush);

	return pDestBrush;
}

IRenderMesh * CBrush::GetRenderMesh(int nLod)
{ 
	IStatObj * pStatObj = m_pStatObj ? m_pStatObj->GetLodObject(nLod) : NULL; 
	return pStatObj ? pStatObj->GetRenderMesh() : NULL;
}

/*
float CBrush::GetLodForDistance(float fDistance)
{
	assert(m_pStatObj!=0);

	float fLodLevel = (float)CObjManager::GetObjectLOD(fDistance, GetLodRatioNormalized(), GetBBox().GetRadius());

	if(fLodLevel > m_pStatObj->m_nLoadedLodsNum-1)
		fLodLevel = (float)m_pStatObj->m_nLoadedLodsNum-1;

	return fLodLevel;
}
*/
void CBrush::OnRenderNodeBecomeVisible()
{
  assert(m_pRNTmpData);
  CRNTmpData::SRNUserData & userData = m_pRNTmpData->userData;

  userData.objMat = m_Matrix;
  float fRadius = GetBBox().GetRadius();

  // init pRenderObject

  CRenderObject * & pObj = userData.pRenderObject;

  assert(!pObj);

	IRenderer *pRenderer = gEnv->pRenderer;
  pObj = pRenderer->EF_GetObject_Perm();

  pObj->m_pRenderNode = this;
  SRenderObjData *pOD = NULL;
/*  if (m_HMAIndex)
  {
    pOD = GetRenderer()->EF_GetObjData(pObj, true);
    pOD->m_HMAData	=	m_HMAIndex;
  }*/
  pObj->m_II.m_Matrix = userData.objMat;  

	pObj->m_nVisAreaStencilRef = 0;
  if(m_pOcNode && m_pOcNode->m_pVisArea)
    pObj->m_II.m_AmbColor = m_pOcNode->m_pVisArea->GetFinalAmbientColor();		
  else 
    pObj->m_II.m_AmbColor = Get3DEngine()->GetSkyColor();
  pObj->m_fAlpha = 1.f;
  pObj->m_ObjFlags |= FOB_INSHADOW | FOB_TRANS_MASK;

  if( !userData.objMat.m01 && !userData.objMat.m02 && !userData.objMat.m10 && !userData.objMat.m12 && !userData.objMat.m20 && !userData.objMat.m21 )
    pObj->m_ObjFlags &= ~FOB_TRANS_ROTATE;
  else
    pObj->m_ObjFlags |= FOB_TRANS_ROTATE;

	if(m_dwRndFlags & ERF_NO_DECALNODE_DECALS)
		pObj->m_ObjFlags |= FOB_NO_STATIC_DECALS;
	else
		pObj->m_ObjFlags &= ~FOB_NO_STATIC_DECALS;

  const Vec3 vCamPos = GetCamera().GetPosition();
  float fEntDistance = cry_sqrtf(Distance::Point_AABBSq(vCamPos,GetBBox()))*m_fRealZoomFactor;

  pObj->m_nSort = fastround_positive(fEntDistance*2.0f);

  pObj->m_pCurrMaterial = GetMaterial();
  if( uint8 nMaterialLayers = GetMaterialLayers() ) 
  {
    uint8 nFrozenLayer = ( nMaterialLayers&MTL_LAYER_FROZEN )? MTL_LAYER_FROZEN_MASK: 0;
    uint8 nWetLayer = ( nMaterialLayers&MTL_LAYER_WET )? MTL_LAYER_WET_MASK: 0;
    pObj->m_nMaterialLayers = (uint32) (nFrozenLayer<< 24) | (nWetLayer<<16);
  }
  else
    pObj->m_nMaterialLayers = 0;

  userData.nLod = CObjManager::GetObjectLOD(fEntDistance, GetLodRatioNormalized(), fRadius);
	
	int nLod = userData.nLod;

	if(CStatObj * pStatObj = (CStatObj *)GetEntityStatObj())
	{
		nLod = CLAMP(nLod, pStatObj->GetMinUsableLod(), (int)pStatObj->m_nMaxUsableLod);
		nLod = pStatObj->FindNearesLoadedLOD(nLod);
	}

	userData.lodDistDissolveTransitionState.nNewLod = userData.lodDistDissolveTransitionState.nOldLod = userData.nLod;
	userData.lodDistDissolveTransitionState.fStartDist = 0.0f;
	userData.lodDistDissolveTransitionState.bFarside = false;
}

void CBrush::PreCopyResources()
{
	if(GetCVars()->e_VoxTer && GetIntegrationType())
	{ 
		// force creation of texture system copy
		if(GetMaterial())
		{
			GetMaterial()->AddRef(); // we will heavily access it from other threads

			SRayHitInfo hitInfo;
			hitInfo.vHitTangent.x = hitInfo.vHitTangent.w = hitInfo.vHitBinormal.y = 1.f;
			ColorF colSumm = Col_Green;
			Vec3 vSummNorm(0.f,0.f,0.5f);

			CIndexedMesh::ProcessMaterial(GetMaterial(), hitInfo.nHitMatID, hitInfo.vHitColor, hitInfo.vHitTC, 0, colSumm, vSummNorm, hitInfo.vHitTangent, hitInfo.vHitBinormal, 0);
			for(int s=0; s<GetMaterial()->GetSubMtlCount(); s++)
        if(GetMaterial()->GetSubMtl(s))
				  CIndexedMesh::ProcessMaterial(GetMaterial()->GetSubMtl(s), hitInfo.nHitMatID, hitInfo.vHitColor, hitInfo.vHitTC, 0, colSumm, vSummNorm, hitInfo.vHitTangent, hitInfo.vHitBinormal, 0);
		}

		// force render mesh indexing 
		if(IStatObj * pStatObj = m_pStatObj ? m_pStatObj->GetLodObject(0) : NULL)
		{
			pStatObj->AddRef(); // we will heavily access it from other threads

			if(GetRenderMesh(0))
				GetRenderMesh(0)->GetTrisForPosition(Vec3(0,0,0), GetMaterial());

			for(int s=0; s<pStatObj->GetSubObjectCount(); s++)
			{
				IStatObj::SSubObject * pSubObj = pStatObj->GetSubObject(s);

				if (pSubObj->pStatObj && pSubObj->nType == STATIC_SUB_OBJECT_MESH && !pSubObj->bHidden)
				{
					pSubObj->pStatObj->AddRef(); // we will heavily access it from other threads

					if(pSubObj->pStatObj->GetRenderMesh())
						pSubObj->pStatObj->GetRenderMesh()->GetTrisForPosition(Vec3(0,0,0),GetMaterial());
				}
			}
		}
	}
}

void CBrush::OffsetPosition(const Vec3& delta)
{
#ifdef SEG_WORLD
	if (m_pRNTmpData) m_pRNTmpData->OffsetPosition(delta);
	m_Matrix.SetTranslation(m_Matrix.GetTranslation() + delta);
	m_WSBBox.Move(delta);

	if (m_pPhysEnt)
	{
		pe_params_pos par_pos;
		par_pos.pos = m_Matrix.GetTranslation();
		m_pPhysEnt->SetParams(&par_pos);
	}
#endif
}
